package com.dycx.message.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dycx.common.Common;
import com.dycx.common.RestResponse;
import com.dycx.common.constants.RegexpConstants;
import com.dycx.common.vendor.kaptcha.KaptchaImage;
import com.dycx.message.constants.MessageTaskConstants;
import com.dycx.message.converter.VerificationCodeConverter;
import com.dycx.message.dto.SendVerificationCodeDTO;
import com.dycx.message.dto.VerificationCodeDTO;
import com.dycx.message.entity.VerificationCodeDO;
import com.dycx.message.mapper.VerificationCodeMapper;
import com.dycx.message.service.MessageService;
import com.dycx.message.service.VerificationCodeService;
import com.dycx.message.vendor.dto.MessageContentDTO;
import com.dycx.message.vendor.dto.RecipientDTO;
import com.dycx.message.vo.VerificationCodeVO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 验证码(VerificationCode)表服务实现类
 *
 * @author wuzongbin
 * @since 2024-01-12 10:39:27
 */

@Service
@AllArgsConstructor
public class VerificationCodeImpl extends ServiceImpl<VerificationCodeMapper, VerificationCodeDO> implements VerificationCodeService {

    private VerificationCodeMapper verificationCodeMapper;
    private MessageService messageService;

    /**
     * 分页查询
     *
     * @param dto 实例对象
     * @return 查询结果
     */
    @Override
    public Page<VerificationCodeVO> getPage(VerificationCodeDTO dto) {
        LambdaQueryWrapper<VerificationCodeDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ObjectUtil.isNotNull(dto.getVerificationCodeId()), VerificationCodeDO::getVerificationCodeId, dto.getVerificationCodeId());
        wrapper.orderByDesc(VerificationCodeDO::getVerificationCodeId);

        Page<VerificationCodeDO> entityPage = page(new Page<>(dto.getPageIndex(), dto.getPageSize()), wrapper);

        Page<VerificationCodeVO> voPage = VerificationCodeConverter.entityToVoPage(entityPage);
        voPage.getRecords().forEach(this::setData);

        return voPage;
    }

    /**
     * 查询详情
     *
     * @param verificationCodeId 主键
     * @return 查询结果
     */
    @Override
    public VerificationCodeVO load(Integer verificationCodeId) {
        VerificationCodeDO entity = getById(verificationCodeId);

        return setData(VerificationCodeConverter.entityToVo(entity));
    }

    /**
     * 设置数据
     *
     * @param vo 实体
     * @return 查询结果
     */
    public VerificationCodeVO setData(VerificationCodeVO vo) {
        return vo;
    }

    /**
     * 添加
     *
     * @param dto
     * @return
     */
    @Override
    public Boolean create(VerificationCodeDTO dto) {
        VerificationCodeDO entity = BeanUtil.toBean(dto, VerificationCodeDO.class);
        return save(entity);
    }

    /**
     * 修改
     *
     * @param dto
     * @return
     */
    @Override
    public Boolean edit(VerificationCodeDTO dto) {
        VerificationCodeDO entity = BeanUtil.toBean(dto, VerificationCodeDO.class);
        return updateById(entity);
    }

    /**
     * 删除
     *
     * @param verificationCodeId 主键
     * @return
     */
    @Override
    public Boolean delete(Integer verificationCodeId) {
        return removeById(verificationCodeId);
    }

    /**
     * 获取最大排序
     *
     * @return 查询结果
     */
    @Override
    public Integer getMaxSort() {
        return verificationCodeMapper.getMaxSort();
    }

    /**
     * 发送验证码
     *
     * @param dto
     * @return
     */
    @Override
    public RestResponse<String> sendVerificationCode(SendVerificationCodeDTO dto) {
        KaptchaImage.verify(dto.getKaptchaCode(), dto.getKaptchaToken());

        // 验证手机号
        Assert.notBlank(dto.getMobile(), "手机号不能为空");

        if (!dto.getMobile().matches(RegexpConstants.MOBILE)) {
            throw new RuntimeException("手机号格式不正确");
        }

        LambdaQueryWrapper<VerificationCodeDO> mobileWrapper = new LambdaQueryWrapper<>();
        mobileWrapper.ge(VerificationCodeDO::getExpiredTime, LocalDateTime.now().minusDays(1));
        mobileWrapper.eq(VerificationCodeDO::getMobile, dto.getMobile());
        if (count(mobileWrapper) >= 10) {
            throw new RuntimeException("同一手机号每天最多可发送10次");
        }

        // 随机生成四位验证码
        String code = Common.createRandom(4, 2);

        LambdaQueryWrapper<VerificationCodeDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(VerificationCodeDO::getMobile, dto.getMobile());
        wrapper.orderByDesc(VerificationCodeDO::getVerificationCodeId);
        wrapper.last("limit 1");

        VerificationCodeDO verificationCodeDO = getOne(wrapper);

        if (verificationCodeDO != null && LocalDateTime.now().isBefore(verificationCodeDO.getExpiredTime().minusMinutes(7))) {
            throw new RuntimeException("验证码已发送，请勿重复发送验证码，2分钟内可发送一次");
        }

        // 发送验证码
        RecipientDTO recipient = new RecipientDTO();
        List<String> phone = new ArrayList<>();
        phone.add(dto.getMobile());

        recipient.setPhone(phone);

        List<String> templateParam = new ArrayList<>();
        templateParam.add(code);
        MessageContentDTO messageContentDTO = new MessageContentDTO();
        messageContentDTO.setSms(templateParam);
        RestResponse<String> restResponse = messageService.sendMessage(MessageTaskConstants.VERIFICATION_CODE, recipient, messageContentDTO);
        if (restResponse.isSuccess()) {
            verificationCodeDO = new VerificationCodeDO();
            verificationCodeDO.setMobile(dto.getMobile());
            verificationCodeDO.setCode(code);
            verificationCodeDO.setVerificationNum(0);
            verificationCodeDO.setExpiredTime(LocalDateTime.now().plusMinutes(10));

            if (!save(verificationCodeDO)) throw new RuntimeException("验证码生成失败");
        }

        return restResponse;
    }

    /**
     * 校验验证码
     *
     * @param dto
     * @return
     */
    @Override
    public Boolean checkVerificationCode(VerificationCodeDTO dto) {
        LambdaQueryWrapper<VerificationCodeDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(VerificationCodeDO::getMobile, dto.getMobile());
        wrapper.orderByDesc(VerificationCodeDO::getVerificationCodeId);
        wrapper.last("limit 1");

        VerificationCodeDO verificationCodeDO = getOne(wrapper);
        Assert.notNull(verificationCodeDO, "请先获取验证码");

        if (verificationCodeDO.getVerificationNum() >= 5) {
            throw new RuntimeException("每个验证码最多可验证5次");
        }

        if (LocalDateTime.now().isAfter(verificationCodeDO.getExpiredTime())) {
            throw new RuntimeException("验证码已过期");
        }

        if (verificationCodeDO.getVerified()) throw new RuntimeException("验证码已使用过，请重新获取");

        if (!verificationCodeDO.getCode().equals(dto.getCode())) {
            verificationCodeDO.setVerificationNum(verificationCodeDO.getVerificationNum() + 1);
            updateById(verificationCodeDO);

            throw new RuntimeException("验证码不正确");
        }

        verificationCodeDO.setVerified(true);
        updateById(verificationCodeDO);

        return true;
    }

}

